/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.googlecode.j4sc.submission;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.stream.XMLStreamException;

import org.apache.commons.lang.StringUtils;

import com.googlecode.j4sc.PartyInformation;
import com.googlecode.j4sc.UserCredentials;
import com.googlecode.j4sc.service.Service;
import com.googlecode.j4sc.submission.audio.Audio;

/**
 * The Submission class represents a submission to the SpinVox Create Service
 * using the {@link Service#submit(Submission)} method
 * <p>
 * A submission is the audio to be converted and associated meta-data.
 * 
 * @author Raj Patel, Dariusz Lorenc
 */
public class Submission {

    private static final Pattern PATTERN = Pattern.compile("\\d{4}-\\d{4}-\\d{4}-\\d{4}");

    private final String accountId;
    private final String applicationName;
    private final String reference;
    private final PartyInformation partyInformation;
    private final Audio audio;
    private final UserCredentials userCredentials;

    /**
     * Construct a new submission
     * 
     * @param accountId
     *            The account-id number provided by SpinVox on your My Account
     *            page. Is a 16 digit number in 4 four digit blocks delimited by
     *            hyphens (e.g. 1234-1234-1234-1234)
     * @param applicationName
     *            the application name
     * @param reference
     *            a unique reference for this submission
     * @param audio
     *            the audio to be submitted for conversion
     * @param userCredentials
     *            the user credentials for your account
     * @throws IllegalArgumentException
     *             thrown if invalid values are provided
     */
    public Submission(String accountId, String applicationName, String reference, Audio audio,
            UserCredentials userCredentials) throws IllegalArgumentException {
        this(accountId, applicationName, reference, audio, null, userCredentials);
    }

    /**
     * Construct a new submission
     * 
     * @param accountId
     *            The account-id number provided by SpinVox on your My Account
     *            page. Is a 16 digit number in 4 four digit blocks delimited by
     *            hyphens (e.g. 1234-1234-1234-1234)
     * @param applicationName
     *            the application name
     * @param reference
     *            a unique reference for this submission
     * @param audio
     *            the audio to be submitted for conversion
     * @param partyInformation
     *            the party information
     * @param userCredentials
     *            the user credentials for your account
     * @throws IllegalArgumentException
     *             thrown if invalid values are provided
     */
    public Submission(String accountId, String applicationName, String reference, Audio audio,
            PartyInformation partyInformation, UserCredentials userCredentials) throws IllegalArgumentException {

        enforceNotEmptyString(accountId, "Account ID");
        enforceNotEmptyString(reference, "Reference");
        enforceNotEmptyString(applicationName, "Application name");
        enforceNotNull(audio, "Audio");
        enforceNotNull(userCredentials, "User Credentials");
        validateAccountId(accountId);

        this.userCredentials = userCredentials;
        this.accountId = accountId;
        this.applicationName = applicationName;
        this.reference = reference;
        this.audio = audio;
        this.partyInformation = partyInformation;
    }

    /**
     * Creates the multipart MIME body of the submission HTTP request. As such,
     * it is not intented to be used by API end users
     * 
     * @return the multipart MIME body
     */
    public String createMimeBody() {
        String xmlPayload;
        try {
            xmlPayload = SubmissionMetaData.createXmlMetadata(accountId, applicationName, reference, partyInformation);
        } catch (XMLStreamException e) {
            throw new InternalException(e);
        }
        return new MultiPartMimeBodyBuilder().addPart().withHeader(MultiPartMimeBodyBuilder.Header.CONTENT_TYPE,
                "text/xml").withContent(xmlPayload).addPart().withHeader(MultiPartMimeBodyBuilder.Header.CONTENT_TYPE,
                "audio/wav").withHeader(MultiPartMimeBodyBuilder.Header.CONTENT_TRANSFER_ENCODING, "base64")
                .withContent(audio.toBase64()).toBody();
    }

    /**
     * Get the user credentials to be used in the submission
     * 
     * @return the user credentials
     */
    public UserCredentials getUserCredentials() {
        return this.userCredentials;
    }

    /**
     * Gets the unique reference for this Submission
     * 
     * @return the unique reference
     */
    public String getReference() {
        return this.reference;
    }

    private static void enforceNotEmptyString(String value, String fieldName) {
        if (StringUtils.isBlank(value)) {
            throw new IllegalArgumentException(fieldName + " must not be null or blank");
        }
    }

    private static void enforceNotNull(Object object, String fieldName) {
        if (object == null) {
            throw new IllegalArgumentException(fieldName + " must not be null");
        }
    }

    private void validateAccountId(String accountId) {
        Matcher matcher = PATTERN.matcher(accountId);
        if (!matcher.matches()) {
            throw new IllegalArgumentException(String.format(
                    "Account ID [%s] is invalid, must be 16 digits broken up into 4 digit parts "
                            + "separated by hyphens e.g. '1234-1234-1234-1234'", accountId));
        }
    }
}
